WICONF.request = {
    showloading: false,
    errorMsg: '网络异常，请稍后再试!'
};
/**
 * @widoc $.request
 * @namespace aux
 * @des 请求数据，要求后台返回的数据结构：
 *   - success {boolean}
 *   - msg {string} success=false 时的错误提示
 *   - data 将作为 onSuccess 的参数
 * 组件配置，在使用组件前调用：
 <pre class="des-conf"> $.setWiConf('request',{
    showloading: false,
    errorMsg: '网络异常，请稍后再试!'
 });</pre>
 * @demo request/demo 发起 ajax 请求获取交互数据
 * @type function
 * @param {object} opts
 * @param {object} opts.ajaxOpts 调用 $.ajax() 请求时的自定义配置，详情参考 jQuery API。
 *   常用属性如下：
 *   - opts.ajaxOpts.url {string} 请求地址
 *   - opts.ajaxOpts.data {object|string} 请求参数，简单参数传 object，复杂参数应先 JSON.stringify()
 * @param {function=} opts.onSuccess(data) 成功回调(参数：数据集合 result.data)，请求成功且 request.success=true 时触发。
 * @param {function=} opts.onError(oErrInfo) 异常回调，请求失败或 request.success!=true 时触发。
 *   - oErrInfo {object}
 *   oErrInfo.type = 'success' 时，oErrInfo 中还包含：msg, result, textStatus, jqXHR
 *   oErrInfo.type = 'error' 时，oErrInfo 中还包含：msg, errorThrown, textStatus, jqXHR
 * @param {boolean|object=} opts.showloading 请求时是否显示 loading 动画，值为 object 时，作为 $.showLoading(opts) 的参数。默认值通过 $.setWiConf() 配置
 * @param {boolean=} opts.argToJson 请求时是否传递复杂参数，设为 true 时附加属性 contentType:"application/json"，并将请求参数格式化为 JSON 字符串，默认为 false。
 * @param {string=} opts.errorMsg 该请求默认的错误提示信息，默认值通过 $.setWiConf() 配置。
 * @return {object} jqXHR 实现了 promise 接口的 jqXHR 对象，详见 jQuery API
 */
$.request = function(opts){
    var CONF = WICONF.request;// 全局配置
    opts = $.extend({
        ajaxOpts: {},
        onSuccess: $.noop,
        //onError: function(oErrInfo),// 未定义时需使用默认操作，故不应设置默认值，在 fHandleErr() 中判断
        showloading: CONF.showloading,
        argToJson: false,
        errorMsg: CONF.errorMsg
    }, opts);

    var oAjaxOpts,// 用户自定义的 $.ajax() 配置
        fSucCus, fErrCus;// success, error 将在组件中重写，因此需要缓存用户自定义的实现

    // 异常处理函数
    var fHandleErr = function(oErrInfo){
        // oErrInfo.type = 'success' 时，oErrInfo 中还包含：msg, result, textStatus, jqXHR
        // oErrInfo.type = 'error' 时，oErrInfo 中还包含：msg, errorThrown, textStatus, jqXHR
        var fOnError = opts.onError,
            sMsg = oErrInfo.msg;
        if(fOnError){
            fOnError(oErrInfo);
        }
        else{
            sMsg && $.showAlert(sMsg);
        }
    };

    // 发起请求
    var fRequest = function(){
        var oShowloading = opts.showloading,
            sLoadingKey;// showLoading() 时生成的 loading 对应的 key
        var fSuccess = function(result, textStatus, jqXHR){
            sLoadingKey && $.hideLoading({key: sLoadingKey});// 传入 key，关闭对应的 loading
            if(result.success){
                opts.onSuccess(result.data);
            }
            else{
                fHandleErr({
                    type: 'success',
                    msg: result.msg || opts.errorMsg,
                    result: result,
                    textStatus: textStatus,
                    jqXHR: jqXHR
                });
            }
            fSucCus && fSucCus(result, textStatus, jqXHR);
        };
        var fError = function(jqXHR, textStatus, errorThrown){
            sLoadingKey && $.hideLoading({key: sLoadingKey});// 传入 key，关闭对应的 loading
            fHandleErr({
                type: 'error',
                msg: opts.errorMsg,
                errorThrown: errorThrown,
                textStatus: textStatus,
                jqXHR: jqXHR
            });
            fErrCus && fErrCus(jqXHR, textStatus, errorThrown);
        };

        // 开启 loading 动画
        if(oShowloading){
            sLoadingKey = $.showLoading(oShowloading).key;// 记录 key，以关闭对应的 loading
        }
        return $.ajax($.extend({
            success: fSuccess,
            error: fError,
            type: 'post',
            dataType: 'json'
        }, oAjaxOpts));
    };

    var fInit = function(){
        var ajaxData;
        oAjaxOpts = opts.ajaxOpts;
        if(opts.argToJson){
            oAjaxOpts.contentType = "application/json";
            ajaxData = oAjaxOpts.data;// 复杂参数需要格式化
            if(typeof ajaxData == 'object'){
                oAjaxOpts.data = JSON.stringify(ajaxData);
            }
        }
        fSucCus = oAjaxOpts.success;
        fErrCus = oAjaxOpts.error;
        delete oAjaxOpts.success;
        delete oAjaxOpts.error;
    };

    fInit();
    return fRequest();// 返回实现了 promise 接口的 jqXHR 对象
};

// TODO del 兼容旧版本，现改为 $.request(opts)
$.fn.request = function(opts, fOnSuccessCus, args, showloading, argToJson){
    var _this = this,
        oReqOpts = {};// $.request(oReqOpts)
    var fOnErrorCus;// 用户自定义的 onError 处理函数
    /* 参数处理 */
    if(Object.prototype.toString.call(opts) === '[object String]'){
        oReqOpts = {
            ajaxOpts: {
                url: opts,
                data: args
            },
            showloading: showloading,
            argToJson: argToJson
            // errorMsg: 旧版本中不支持此属性的设置，故忽略
            // onSuccess: 因为旧版本中回调的 this 需指向 _this，因此在调用 $.request() 前才处理
            // onError: 旧版本中不支持此属性的设置，故忽略
        };
    }
    else if(opts){
        oReqOpts = {
            ajaxOpts: opts.ajaxOpts,
            showloading: opts.showloading,
            argToJson: opts.argToJson,
            errorMsg: opts.errorMsg
            // onSuccess: 同上
            // onError: 因为旧版本中 onError 接收的参数不同，因此需要单独处理
        };
        fOnSuccessCus = opts.drawDomFn;// 旧版本中接口为 opts.drawDomFn，对应新版本中 onSuccess
        fOnErrorCus = opts.onError;
        if(fOnErrorCus){
            oReqOpts.onError = function(oErrInfo){
                switch(oErrInfo.type){
                    case 'success':
                        fOnErrorCus(oErrInfo.result, oErrInfo.msg);
                        break;
                    case 'error':
                        fOnErrorCus(oErrInfo.jqXHR, oErrInfo.textStatus, oErrInfo.errorThrown, oErrInfo.msg);
                        break;
                }
            };
        }
    }
    if(fOnSuccessCus){
        oReqOpts.onSuccess = function(data){
            fOnSuccessCus.call(_this, data);
        };
    }
    $.request(oReqOpts);

    return _this;
};